;
;MODEM.ASM V3.0, BY WARD CHRISTENSEN
;
;CP/M - CP/M FILE TRANSFER PROGRAM, AND
;TERMINAL PROGRAM.
;
; * * * * * * * * * * * * * * * * * * * * * * * *
;						*
;    THIS PROGRAM DOCUMENTED IN "MODEM.DOC"	*
;						*
; * * * * * * * * * * * * * * * * * * * * * * * *
; THIS PROGRAM WAS "MODEM.ASM" BUT IS 		*
; TEMPORARILY NAMED "MODEM2.ASM" SO PEOPLE	*
; WILL REALIZE IT IS AN ENHANCEMENT OF		*
; THE ORIGINAL PROGRAM "MODEM.ASM" ON CP/M	*
; USER'S GROUP DISK 25.				*
; * * * * * * * * * * * * * * * * * * * * * * * *
;
;PLEASE PASS ON MODS, BUGS, ETC, SO YOUR
;FIXES OR ENHANCEMENTS MAY BE SHARED BY ALL,
;	     TO:
;	Ward Christensen
;	688 E. 154th St. #5D
;	Dolton, Il. 60419
;
;	(312) 849-6279
;
;You may send a self-addressed stamped postcard
;to be informed of changes/bugs as they become
;known.
;
;	--------------
;
;	09/23/77
;ORIGINALLY WRITTEN BY WARD CHRISTENSEN
;
;	04/26/79
;REWRITTEN BY WARD CHRISTENSEN TO COMBINE
;IMPROVEMENTS TO THE ORIGINAL MADE BY WARD
;AND BY KEITH PETERSEN, W8SDZ, AND SUGGESTIONS
;BY JIM BELL WHICH KEITH IMPLEMENTED.  SEE
;MODEM.DOC FOR ADDITIONAL HISTORICAL
;INFORMATION AND DOCUMENTATION.
;
;	05/09/79
;ALLOW 'T' AND 'E' SUB-OPTIONS TO GO TO TERMINAL
;OR ECHO MODEM AFTER TRANSFERRING A FILE.  (WLC)
;
;	05/22/79
;ADD FEATURE TO MAKE RECEIVE FILE ROUTINE SAY
;FILE SUCCESSFULLY OPENED, WHEN IN QUIET MODE.
;MOVE INITIAL GOBBLE GARBAGE INPUTS TO BEFORE
;COMMAND CPI'S SO ALL MODES ARE CLEARED. CHANGE
;INITIAL SEND WAIT TO 80 SECS TO ALLOW MORE TIME
;FOR RECEIVING END TO COME UP. ADD 'H' AFTER MSG
;THAT SHOWS NUMBER OF SECTORS IN EXTENT ABOUT TO
;BE SENT.  (KBP)
;
;	05/24/79
;FIX MISSING RETURN INSTRUCTION AT END OF
;INITIALIZATION ROUTINE.  (KBP)
;
;	07/13/79
;PUT IN RESET OF DMA ADDR TO 80H AFTER
;FILE RECEIVE, BECAUSE "SUBMIT" UNDER CP/M
;DOESN'T RESET IT BEFORE READING THE
;NEXT SUBMITTED COMMAND. /// ALSO DELETE
;CODE FOR "CANCEL" AS IT'S JUST TOO EASY
;TO INTENTIONALLY GET A CANCEL AS A
;GARBAGED LINE CHARACTER.
;
;	07/07/82 17:41:54  mods for cp/m by rjs
;
	CANFLG	EQU	0ffh	;nO CANCEL ABILITY
;
PMMI	EQU	0	;TRUE, IS PMMI
;
MODCTLP	EQU	0C0H	;PMMI VALUES
MODSNDB	EQU	1	;BIT TO TEST FOR SEND
MODSNDR	EQU	1	;VALUE WHEN READY
MODRCVB	EQU	2	;BIT TO TEST FOR RECEIVE
MODRCVR	EQU	2	;VALUE WHEN READY
MODDATP	EQU	0C1H	;DATA PORT
BAUDRP	EQU	0C2H	;BAUD RATE OUTPUT
MODCTL2	EQU	0C3H	;SECOND CTL PORT
;
ORIGMOD	EQU	1DH	;8 DATA, NO PARITY, ORIG
ANSWMOD	EQU	1EH	;8 DATA, NO PARITY, ANSW
;
INITREQ	EQU	0	;MODEM INIT. REQ'D?
INITC1	EQU	64H	;FIRST INIT CHAR TO CTL PORT
INITC2	EQU	64H	;2ND INIT CHAR TO CTL PORT
;
ERRLIM	EQU	10	;MAX ALLOWABLE ERRORS
EXITCHR	EQU	'E'-40H ;CTL-E EXIT FROM T OR C
DISCCHR	EQU	'D'-40H	;CTL-D DISCONNECTS MODEM T/C
;
FASTCLK	EQU	1	;PUT 1 HERE FOR 4 MHZ CLOCK
; 
;DEFINE ASCII CHARACTERS USED
;
SOH	EQU	1	;START OF HEADER
EOT	EQU	4	;END OF TRANSMISSION
ACK	EQU	6	;ACKNOWLEDGE
NAK	EQU	15H	;NEG ACKNOWLEDGE
CAN	EQU	18H	;CANCEL
LF	EQU	10	;LINEFEED
CR	EQU	13	;CARRIAGE RETURN
; 
	ORG	100H
	CALL START	;GO PRINT ID
	DB	'MODEM PROGRAM V3.0 AS OF '
	DB	'07/07/82',CR,LF,'$'
; 
START:	POP DX	;GET ID MESSAGE
	MOV CL,PRINT
	CALL BDOS	;PRINT ID MESSAGE
;
;
;SAVE PRIMARY OPTION, VALIDATE SECONDARY OPT.
;
	CALL PROCOPT
;
;INIT THE MODEM OR SERIAL PORT
;
	CALL INITMOD
;
;MOVE THE FILENAME FROM FCB 2 TO FCB 1
;
	CALL MOVEFCB
;
;GOBBLE UP GARBAGE CHARS FROM THE LINE
;PRIOR TO RECEIVE OR SEND
;
	IN AL,MODDATP
	IN AL,MODDATP
;
;JMP TO APPROPRIATE FUNCTION
;
	MOV AL,OPTION	;GET PRIMARY OPTION
;
	CMP AL,'C'	;(COMPAT W/EARLIER
	JNZ L@00001
	JMP TRMECHO	;OPTION "COMPUTER")
L@00001:
;
	CMP AL,'E'	;TERMINAL IN ECHO
	JNZ L@00002
	JMP TRMECHO	;..MODE?
L@00002:
;
	CMP AL,'T'	;TERMINAL..
	JNZ L@00003
	JMP TERM	;..MODE?
L@00003:
;
	CMP AL,'D'
	JNZ L@00004
	JMP DISCONN
L@00004:
;
	CMP AL,'X'
	JNZ L@00005
	JMP EXAM	;GIVE EXAMPLES
L@00005:
;
	CMP AL,'S'	;SEND..
	JNZ L@00006
	JMP SENDFIL	;..A FILE?
L@00006:
;
	CMP AL,'R'	;RECEIVE..
	JNZ L@00007
	JMP RCVFIL	;..A FILE?
L@00007:
;
;INVALID OPTION
;
	JMP BADOPT
; * * * * * * * * * * * * * * * * * * * *
;					*
;	TERM: TERMINAL MODE		*
;					*
; * * * * * * * * * * * * * * * * * * * *
;
;THIS PROGRAM SIMPLY SENDS KEYED CHARACTERS
;DOWN THE LINE, AND DISPLAYS CHARACTERS
;RECEIVED FROM THE LINE.  THIS MAKES IT
;SUITABLE FOR COMMUNICATION WITH TIME SHARING
;COMPUTERS, CBBS'S, OR ANOTHER PROGRAM
;RUNING "MODEM E" (ECHO MODE)
;
;tipe THE "EXITCHR" (ORIGINALLY CTL-E) TO EXIT.
;OR THE "DISCCHR" (ORIGINALLY CTL-D) TO DISCONN.
;
;A FUTURE ENHANCEMENT WILL BE TO WRITE THE
;RECEIVED DATA IN MEMORY, AND ALLOW IT TO
;BE WRITTEN TO DISK
;
TERM:	CALL STAT	;LOCAL CHAR KEYED?
	JNZ L@00008
	JMP TERML	;..NO, CHECK LINE
L@00008:
	CALL KEYIN	;GET CHAR
	CMP AL,EXITCHR	;TIME TO END?
	JNZ L@00009
	JMP CKDIS	;YES, CK DISCONN
L@00009:
	CMP AL,DISCCHR	;DISCONNECT REQUEST?
	JNZ L@00010
	JMP DISCONN	;YES, DO IT
L@00010:
	OUT MODDATP,AL	;SEND THE CHAR
;
;SEE IF CHAR FROM LINE
;
TERML:	IN AL,MODCTLP	;READ STATUS
	AND AL,MODRCVB	;ISOLATE BIT
	CMP AL,MODRCVR	;READY?
	JZ L@00011
	JMP TERM	;..NO, LOOP
L@00011:
	IN AL,MODDATP	;READ DATA
	CALL tipe	;tipe IT
	JMP TERM	;LOOP
;
; * * * * * * * * * * * * * * * * * * * *
;					*
;	TRMECHO: TERMINAL WITH ECHO	*
;					*
; * * * * * * * * * * * * * * * * * * * *
;
;TERMINAL PROGRAM WITH ECHO - SEE NOTES
;UNDER "TERM" ABOVE
;
;C A U T I O N   DON'T RUN WITH BOTH COMPUTERS
;IN "ECHO" MODE - LINE ERRORS (OR ANY CHAR)
;WILL BE ECHOED BACK AND FORTH AD INFINITUM.
;
TRMECHO:	IN AL,MODCTLP	;GET STATUS
	AND AL,MODRCVB	;ISOLATE READY BIT
	CMP AL,MODRCVR	;ARE WE READY?
	JNZ L@00012
	JMP LINECHR	;YES, READ THE CHR
L@00012:
	CALL STAT	;CHECK LOCAL KB
	JNZ L@00013
	JMP TRMECHO	;..NO CHAR
L@00013:
	CALL KEYIN	;GET LOCAL CHAR
	CMP AL,EXITCHR	;END?
	JNZ L@00014
	JMP CKDIS	;YES, CK DISCONN, EXIT
L@00014:
	CMP AL,DISCCHR	;DISCONN?
	JNZ L@00015
	JMP DISCONN	;..YES, DO IT.
L@00015:
	OUT MODDATP,AL	;SEND CHAR
	CALL tipe	;ECHO IT LOCALLY
	JMP TRMECHO	;..AND LOOP
;
;GOT CHAR FROM LINE
;
LINECHR:	IN AL,MODDATP	;GET CHAR
	OUT MODDATP,AL	;ECHO IT
	CALL tipe	;tipe IT
	JMP TRMECHO	;LOOP
;
; * * * * * * * * * * * * * * * * * * * *
;					*
;	SENDFIL: SENDS A CP/M FILE	*
;					*
; * * * * * * * * * * * * * * * * * * * *
;
;THE CP/M FILE SPECIFIED IN THE MODEM COMMAND
;IS TRANSFERRED OVER THE PHONE TO ANOTHER
;COMPUTER RUNNING MODEM WITH THE "R" (RECEIVE)
;OPTION.  THE DATA IS SENT ONE SECTOR AT A
;TIME WITH HEADERS AND CHECKSUMS, AND RE-
;TRANSMISSION ON ERRORS.  
;
SENDFIL:	CALL OPENFIL	;OPEN THE FILE
	MOV DL,80	;WAIT 80 SEC..
	CALL WAITNAK	;..FOR INITIAL NAK
SENDLP:	CALL RDSECT	;READ A SECTOR
	JAE L@00016
	JMP SENDEOF	;SEND EOF IF DONE
L@00016:
	CALL INCRSNO	;BUMP SECTOR #
	XOR AL,AL	;ZERO ERROR..
	MOV  ERRCT,AL	;..COUNT
SENDRPT:	CALL SENDHDR	;SEND A HEADER
	CALL SENDSEC	;SEND DATA SECTOR
	CALL SENDCKS	;SEND CKSUM
	CALL GETACK	;GET THE ACK
	JAE L@00017
	JMP SENDRPT	;REPEAT IF NO ACK
L@00017:
	JMP SENDLP	;LOOP UNTIL EOF
;
;FILE SENT, SEND EOT'S
;
SENDEOF:	MOV AL,EOT	;SEND..
	CALL SEND	;..AN EOT
	CALL GETACK	;GET THE ACK
	JAE L@00018
	JMP SENDEOF	;LOOP IF NO ACK
L@00018:
	JMP DONE	;ALL DONE
;
; * * * * * * * * * * * * * * * * * * * *
;					*
;	RCVFIL: RECEIVE A FILE		*
;					*
; * * * * * * * * * * * * * * * * * * * *
;
;RECEIVES A FILE IN BLOCK FORMAT AS SENT
;BY ANOTHER PERSON DOING "MODEM S FN.FT".
;
RCVFIL:	CALL ERASFIL	;ERASE THE FILE
	CALL MAKEFIL	;..THEN MAKE NEW
	MOV AL,QFLG	;SEE IF IN QUIET MODE
	OR AL,AL
	JZ L@00019
	JMP RCVLP	;NOT IN QUIET MODE, SKIP MSG
L@00019:
	CALL ILPRT	;PRINT:
	DB	'FILE OPEN, READY TO RECEIVE',CR,LF,0
RCVLP:	CALL RCVSECT	;GET A SECTOR
	JAE L@00020
	JMP RCVEOT	;GOT EOT
L@00020:
	CALL WRSECT	;WRITE THE SECTOR
	CALL INCRSNO	;BUMP SECTOR #
	CALL SENDACK	;ACK THE SECTOR
	JMP RCVLP	;LOOP UNTIL EOF
;
;GOT EOT ON SECTOR - FLUSH BUFFERS, END
;
RCVEOT:	CALL WRBLOCK	;WRITE THE LAST BLOCK
	CALL SENDACK	;ACK THE SECTOR
	CALL CLOSFIL	;CLOSE THE FILE
	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00021
	JMP CKDIS	;YES, CK DISCONN, END.
L@00021:
	JMP DONE	;DISCONN IF REQ'D
;
; * * * * * * * * * * * * * * * * * * * *
;					*
;		SUBROUTIES		*
;					*
; * * * * * * * * * * * * * * * * * * * *
;
;
;---->	RCVSECT: RECEIVE A SECTOR
;
;RETURNS WITH CARRY SET IF EOT RECEIVED.
;
RCVSECT:	XOR AL,AL	;GET 0
	MOV  ERRCT,AL	;INIT ERROR COUNT
RCVRPT:	MOV AL,QFLG	;QUIET?
	OR AL,AL
	JNZ L@00022
	JMP RCVSQ	;YES, NO STAT MSG.
L@00022:
	CALL ILPRT	;PRINT:
	DB	'AWAITING #',0
	MOV AL,SECTNO	;GET SECTOR #
	INC AL	;(REAL INR LATER)
	CALL HEXO	;PRINT IN HEX
	CALL CRLF	;..THEN CRLF
;
RCVSQ:	MOV CH,10	;10 SEC TIMEOUT
	CALL RECV	;GET SOH/EOT
	JAE L@00023
	JMP RCVSTOT	;TIMEOUT
L@00023:
	CMP AL,SOH	;GET SOH?
	JNZ L@00024
	JMP RCVSOH	;..YES
L@00024:
;
;EARLIER VERS. OF MODEM PROG SENT SOME NULLS -
;IGNORE THEM
;
	OR AL,AL	;00 FROM SPEED CHECK?
	JNZ L@00025
	JMP RCVSQ	;YES, IGNORE IT
L@00025:
	CMP AL,EOT	;END OF TRANSFER?
	STC	;RETURN WITH CARRY..
	JNZ L@00026
	RET	;..SET IF EOT
L@00026:
;
;DIDN'T GET SOH  OR EOT - 
;
	MOV CH,AL	;SAVE CHAR
	MOV AL,VSEEFLG	;VIEWING..
	OR AL,AL	;..MODE?
	JNZ L@00027
	JMP RCVSEH	;YES, PRT.MSG
L@00027:
	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00028
	JMP RCVSERR	;YES, SKIP MSG
L@00028:
RCVSEH:	MOV AL,CH	;GET CHAR
	CALL HEXO	;SHOW IN HEX
	CALL ILPRT	;PRINT:
	DB	'H RCD, NOT SOH',CR,LF,0
;
;DIDN'T GET VALID HEADER - PURGE THE LINE,
;THEN SEND NAK.
;
RCVSERR:	MOV CH,1	;WAIT FOR 1 SEC..
	CALL RECV	;..WITH NO CHARS
	JNAE L@00029
	JMP RCVSERR	;LOOP UNTIL SENDER DONE
L@00029:
	MOV AL,NAK	;SEND..
	CALL SEND	;..THE NAK
	MOV AL,ERRCT	;ABORT IF..
	INC AL	;..WE HAVE REACHED..
	MOV  ERRCT,AL	;..THE ERROR..
	CMP AL,ERRLIM	;..LIMIT?
	JAE L@00030
	JMP RCVRPT	;..NO, TRY AGAIN
L@00030:
;
;10 ERRORS IN A ROW - 
;
	MOV AL,VSEEFLG	;VIEWING..
	OR AL,AL	;..FILE?
	JNZ L@00031
	JMP RCVCKQ	;YES, ASK RETRY/QUIT
L@00031:
	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00032
	JMP RCVSABT	;ABORT
L@00032:
RCVCKQ:	CALL CKQUIT	;RETRY/QUIT?
	JNZ L@00033
	JMP RCVSECT	;TRY AGAIN
L@00033:
;
RCVSABT:	CALL CLOSFIL	;KEEP WHATEVER WE GOT
	CALL ERXIT
	DB	'++UNABLE TO RECEIVE BLOCK'
	DB	CR,LF,'++ABORTING++$'
;
;TIMEDOUT ON RECEIVE
;
RCVSTOT:	MOV AL,VSEEFLG	;VIEWING..
	OR AL,AL	;..MODE?
	JNZ L@00034
	JMP RCVSPT	;YES, PRT MSG
L@00034:
	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00035
	JMP RCVSERR	;YES, NO MSG
L@00035:
RCVSPT:	CALL ILPRT
	DB	'++TIMEOUT++ ',0
RCVPRN:	MOV AL,ERRCT	;PRINT ERROR..
	CALL HEXO	;..COUNT
	CALL CRLF
	JMP RCVSERR	;BUMP ERR CT, ETC.
;
;GOT SOH - GET BLOCK #, BLOCK # COMPLEMENTED
;
RCVSOH:	MOV CH,1	;TIMEOUT = 1 SEC
	CALL RECV	;GET SECTOR
	JAE L@00036
	JMP RCVSTOT	;GOT TIMEOUT
L@00036:
	MOV DH,AL	;D=BLK #
	MOV CH,1	;TIMEOUT = 1 SEC
	CALL RECV	;GET CMA'D SECT #
	JAE L@00037
	JMP RCVSTOT	;TIMEOUT
L@00037:
	NOT AL	;CALC COMPLEMENT
	CMP AL,DH	;GOOD SECTOR #?
	JNZ L@00038
	JMP RCVDATA	;YES, GET DATA
L@00038:
;
;GOT BAD SECTOR #
;
	MOV AL,VSEEFLG	;VIEWING..
	OR AL,AL	;..MODE?
	JNZ L@00039
	JMP RCVBSE	;..YES, PRT MSG
L@00039:
	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00040
	JMP RCVSERR	;..YES, NO MSG
L@00040:
;
RCVBSE:	CALL ILPRT	;PRINT:
	DB	'++BAD SECTOR # IN HDR',CR,LF,0
	JMP RCVSERR	;BUMP ERROR CT.
;
;
RCVDATA:	MOV AL,DH	;GET SECTOR #
	MOV  RCVSNO,AL	;SAVE IT
	MOV AL,1	;SHOW..
	MOV  DATAFLG,AL	;GETTING DATA
	MOV CL,0	;INIT CKSUM
	MOV BX,OFFSET 80H	;POINT TO BUFFER
RCVCHR:	MOV CH,1	;1 SEC TIMEOUT
	CALL RECV	;GET CHAR
	JAE L@00041
	JMP RCVSTOT	;TIMEOUT
L@00041:
	MOV BYTE PTR [BX],AL	;STORE CHAR
	INC BL	;DONE?
	JZ L@00042
	JMP RCVCHR	;NO, LOOP
L@00042:
;
;VERIFY CHECKSUM
;
	MOV DH,CL	;SAVE CHECKSUM
	XOR AL,AL	;SHOW..
	MOV  DATAFLG,AL	;..END OF DATA
	MOV CH,1	;TIMEOUT LEN.
	CALL RECV	;GET CHECKSUM
	JAE L@00043
	JMP RCVSTOT	;TIMEOUT
L@00043:
	CMP AL,DH	;CHECKSUM OK?
	JZ L@00044
	JMP RCVCERR	;NO, ERROR
L@00044:
;
;GOT A SECTOR, IT'S A DUP IF = PREV,
;	OR OK IF = 1 + PREV SECTOR
;
	MOV AL,RCVSNO	;GET RECEIVED
	MOV CH,AL	;SAVE IT
	MOV AL,SECTNO	;GET PREV
	CMP AL,CH	;PREV REPEATED?
	JNZ L@00045
	JMP RECVACK	;ACK TO CATCH UP
L@00045:
	INC AL	;CALC NEXT SECTOR #
	CMP AL,CH	;MATCH?
	JZ L@00046
	JMP ABORT	;NO MATCH - STOP SENDER, EXIT
L@00046:
	RET	;CARRY OFF - NO ERRORS
;
;GOT CKSUM
;
RCVCERR:	MOV AL,VSEEFLG	;VIEWING..
	OR AL,AL	;..MODE?
	JNZ L@00047
	JMP RCVCPR	;..YES, PRT MSG
L@00047:
	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00048
	JMP RCVSERR	;YES, NO MSG
L@00048:
RCVCPR:	CALL ILPRT
	DB	'++CKSUM++ ',0
	JMP RCVPRN	;PRINT ERROR #
;
;PREV SECT REPEATED, DUE TO THE LAST ACK
;BEING GARBAGED.  ACK IT SO SENDER WILL CATCH UP 
;
RECVACK:	CALL SENDACK	;SEND THE ACK,
	JMP RCVSECT	;GET NEXT BLOCK
;
;SEND AN ACK FOR THE SECTOR
;
SENDACK:	MOV AL,ACK	;GET ACK
	CALL SEND	;..AND SEND IT
	RET
;
;---->	SENDHDR: SEND THE SECTOR HEADER
;
;SEND: (SOH) (BLOCK #) (COMPLEMENTED BLOCK #)
;
SENDHDR:	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00049
	JMP SENDHNM	;YES, SKIP STATUS MSG.
L@00049:
	CALL ILPRT	;PRINT:
	DB	'SEND # ',0
	MOV AL,SECTNO	;PRINT..
	CALL HEXO	;..SECT #
	CALL CRLF	;..THEN CR/LF
;
SENDHNM:	MOV AL,SOH	;SEND..
	CALL SEND	;..SOH,
	MOV AL,SECTNO	;THEN SEND..
	CALL SEND	;..SECTOR #
	MOV AL,SECTNO	;THEN SENDOR #
	NOT AL	;..COMPLEMENTED..
	CALL SEND	;..SECTOR #
	RET	;FROM SENDHDR
;
;---->	SENDSEC: SEND THE DATA SECTOR
;
;WHILE SENDING THE SECTOR, THE "DATAFLG" IS SET
;SUCH THAT IF "V" (VIEW THE FILE) WAS REQUESTED,
;THE "SHOW" ROUTINE WILL PRINT THE DATA, BUT NOT
;THE HDR OR CKSUM, OR ANY NON-FATAL MSGS.
;
SENDSEC:	MOV AL,1	;SHOW NOW AT DATA..
	MOV  DATAFLG,AL	;..FOR VIEW COMMAND
	MOV CL,0	;INIT CKSUM
	MOV BX,OFFSET 80H	;POINT TO BUFFER
SENDC:	MOV AL,BYTE PTR [BX]	;GET A CHAR
	CALL SEND	;SEND IT
	INC BL	;POINT TO NEXT CHAR
	JZ L@00050
	JMP SENDC	;LOOP IF <100H
L@00050:
	XOR AL,AL	;SHOW NOT INTO DATA..
	MOV  DATAFLG,AL	;..FOR VIEW COMMAND
	RET	;FROM SENDSEC
;
;---->	SENDCKS: SEND THE CHECKSUM
;
SENDCKS:	MOV AL,CL	;SEND THE..
	CALL SEND	;..CHECKSUM
	RET	;FROM SENDCKS
;
;---->	GETACK: GET THE ACK ON THE SECTOR
;
;RETURNS WITH CARRY CLEAR IF ACK RECEIVED.
;IF AN ACK IS NOT RECEIVED, THE ERROR COUNT
;IS INCREMENTED, AND IF LESS THAN "ERRLIM",
;CARRY IS SET AND CONTROL RETURNS.  IF THE
;ERROR COUNT IS AT "ERRLIM", THE PROGRAM
;ABORTS IF IN "QUIET" MODE, OR ASKS THE
;USER FOR QUIT/RETRY IF NOT.
;
GETACK:	MOV CH,10	;WAIT 10 SECONDS MAX
	CALL RECVDG	;RECV W/GARBAGE COLLECT
	JAE L@00051
	JMP GETATOT	;TIMED OUT
L@00051:
	CMP AL,ACK	;OK? (CARRY OFF IF =)
	JNZ L@00052
	RET	;YES, RET FROM GETACK
L@00052:
;
	IF CANFLG
	CMP AL,CAN	;CANCEL TRANSMISSION?
	JNZ L@00053
	JMP ABORT	;..YES
L@00053:
	ENDIF
;
	MOV CH,AL	;SAVE CHAR
	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00054
	JMP ACKERR	;..YES, NO MSG
L@00054:
	MOV AL,CH	;GET CHAR
	CALL HEXO	;PRINT IN HEX
	CALL ILPRT	;PRINT:
	DB	'H RCD, NOT ACK',CR,LF,0
;
;TIMEOUT OR ERROR ON ACK - BUMP ERROR COUNT
;
ACKERR:	MOV AL,ERRCT	;GET COUNT
	INC AL	;BUMP IT
	MOV  ERRCT,AL	;SAVE BACK
	CMP AL,ERRLIM	;AT LIMIT?
	JNB L@00055
	RET	;NOT AT LIMIT
L@00055:
;
;REACHED ERROR LIMIT
;
	MOV AL,VSEEFLG	;VIEWING..
	OR AL,AL	;..FILE?
	JNZ L@00056
	JMP GACKV	;YES, ASK QUIT/RETRY
L@00056:
	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00057
	JMP CSABORT	;..YES, NO MCG
L@00057:
GACKV:	CALL CKQUIT	;SEE IF WANT TO QUIT
	STC	;TO SHOW NO ACK
	JNZ L@00058
	RET	;KEEP ON TRYIN'
L@00058:
CSABORT:	CALL ERXIT
	DB	'CAN''T SEND SECTOR '
	DB	'- ABORTING',CR,LF,'$'
;
;TIMEOUT GETTING ACK
;
GETATOT:	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00059
	JMP ACKERR	;YES, NO MSG
L@00059:
	CALL ILPRT	;PRINT:
	DB	'TIMEOUT ON ACK',0DH,0AH,0
	JMP ACKERR
; 
;---->	CKABORT: CHECK FOR LOCAL ABORT
;
;IF THE USER WANTS TO CANCEL THE TRANSMISSION,
;TYPING CTL-X WILL ABORT IT.  NOTE THIS TEST
;IS NOT MADE IF IN QUIET (NO CONSOLE I/O) MODE.
;
CKABORT:	MOV AL,VSEEFLG	;VIEWING?
	OR AL,AL
	JNZ L@00060
	JMP CKABGO	;YES, CHECK
L@00060:
	MOV AL,QFLG	;SUPPRESSED..
	OR AL,AL	;..CONSOLE I/O?
	JNZ L@00061
	RET	;YES, NO TEST
L@00061:
;
	IF NOT CANFLG
	RET
	ENDIF
;
CKABGO:	CALL STAT	;KEY PRESSED?
	JNZ L@00062
	RET	;NOTHING FROM KEYBOARD
L@00062:
	CALL KEYIN	;GET DATA
	CMP AL,EXITCHR	;TIME TO END?
	JZ L@00063
	RET	;NO, CONTINUE
L@00063:
; 
ABORT:	
ABORTL:	MOV CH,1	;1 SEC. W/O CHARS.
	CALL RECV
	JNAE L@00064
	JMP ABORTL	;LOOP UNTIL SENDER DONE
L@00064:
	MOV AL,CAN	;CONTROL X
	CALL SEND	;STOP SENDING END
ABORTW:	MOV CH,1	;1 SEC W/O CHARS.
	CALL RECV
	JNAE L@00065
	JMP ABORTW	;LOOP UNTIL SENDER DONE
L@00065:
	MOV AL,' '	;GET A SPACE...
	CALL SEND	;TO CLEAR OUT CONTROL X
	CALL ILPRT	;EXIT WITH ABORT MSG
	DB	'MODEM PROGRAM CANCELLED',0DH,0AH,0
	JMP CKDIS	;CHECK FOR DISCONN.
;
;---->	INCRSNO: INCREMENT SECTOR #
;
INCRSNO:	MOV AL,SECTNO	;INCR..
	INC AL	;..SECT..
	MOV  SECTNO,AL	;..NUMBER
	RET
;
;---->	ERASFIL: ERASE THE INCOMING FILE.
;
;IF IT EXISTS, ASK IF IT MAY BE ERASED.
;
ERASFIL:	MOV DX,OFFSET FCB	;POINT TO CTL BLOCK
	MOV CL,SRCHF	;SEE IF IT..
	CALL BDOS	;..EXISTS
	INC AL	;FOUND?
	JNZ L@00066
	RET	;..NO, RETURN
L@00066:
	CALL ILPRT	;PRINT:
	DB	'++FILE EXISTS, tipe Y TO ERASE: ',0
	CALL KEYIN	;GET CHAR
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH
	CALL tipe	;ECHO
	POP AX
	XCHG AL,AH
	SAHF
	AND AL,5FH	;MAKE UPPER CASE
	CMP AL,'Y'	;WANT ERASED?
	JZ L@00067
	JMP CKDIS	;QUIT IF NOT ERASE
L@00067:
	CALL CRLF	;BACK TO START OF LINE
;
;ERASE OLD FILE
;
	MOV DX,OFFSET FCB	;POINT TO FCB
	MOV CL,ERASE	;GET BDOS FNC
	CALL BDOS	;DO THE ERASE
	RET	;FROM "ERASFIL"
;
;---->	MAKEFIL: MAKES THE FILE TO BE RECEIVED
;
MAKEFIL:	MOV DX,OFFSET FCB	;POINT TO FCB
	MOV CL,MAKE	;GET BDOS FNC
	CALL BDOS	;TO THE MAKE
	INC AL	;FF=BAD?
	JZ L@00068
	RET	;OPEN OK
L@00068:
;DIRECTORY FULL - CAN'T MAKE FILE
	CALL ERXIT
	DB	'++ERROR - CAN''T MAKE FILE',CR,LF
	DB	'++DIRECTORY MUST BE FULL',CR,LF,'$'
;
;---->	OPENFIL: OPENS THE FILE TO BE SENT
;
OPENFIL:	MOV DX,OFFSET FCB	;POINT TO FILE
	MOV CL,OPEN	;GET FUNCTION
	CALL BDOS	;OPEN IT
	INC AL	;OPEN OK?
	JZ L@00069
	JMP OPENOK	;..YES
L@00069:
	CALL ERXIT	;..NO, ABORT
	DB	'CAN''T OPEN FILE$'
;
OPENOK:	CALL ILPRT	;PRINT:
	DB	'FILE OPEN, EXTENT LENGTH: ',0
	MOV AL,FCB+15	;GET # SECTORS
	CALL HEXO	;PRINT IN HEX
	MOV AL,'H'
	CALL tipe	;PRINT 'H' AFTER NUMBER
	CALL CRLF	;..THEN CRLF
	RET
;
;---->	CLOSFIL: CLOSES THE RECEIVED FILE
;
CLOSFIL:	MOV DX,OFFSET FCB	;POINT TO FILE
	MOV CL,CLOSE	;GET FUNCTION
	CALL BDOS	;CLOSE IT
	INC AL	;CLOSE OK?
	JZ L@00070
	RET	;..YES, RETURN
L@00070:
	CALL ERXIT	;..NO, ABORT
	DB	'CAN''T CLOSE FILE$'
;
;---->	RDSECT: READS A SECTOR
;
;FOR SPEED, THIS ROUTINE BUFFERS UP 16
;SECTORS AT A TIME.
;
RDSECT:	MOV AL,SECINBF	;GET # SECT IN BUFF.
	DEC AL	;DECREMENT..
	MOV  SECINBF,AL	;..IT
	JNS L@00071
	JMP RDBLOCK	;EXHAUSTED?  NEED MORE.
L@00071:
	MOV BX,SECPTR	;GET POINTER
	MOV DX,OFFSET 80H	;TO DATA 
	CALL MOVE128	;MOVE TO BUFFER
	MOV SECPTR,BX	;SAVE BUFFER POINTER
	RET	;FROM "READSEC"
;
;BUFFER IS EMPTY - READ IN ANOTHER BLOCK OF 16
;
RDBLOCK:	MOV AL,EOFLG	;GET EOF FLAG
	CMP AL,1	;IS IT SET/
	STC	;TO SHOW EOF
	JNZ L@00072
	RET	;GOT EOF
L@00072:
	MOV CL,0	;SECTORS IN BLOCK
	MOV DX,OFFSET DBUF	;TO DISK BUFFER
RDSECLP:	PUSH CX
	PUSH DX
	MOV CL,STDMA	;SET DMA..
	CALL BDOS	;..ADDR
	MOV DX,OFFSET FCB
	MOV CL,READ
	CALL BDOS
	POP DX
	POP CX
	OR AL,AL	;READ OK?
	JNZ L@00073
	JMP RDSECOK	;YES
L@00073:
	DEC AL	;EOF?
	JNZ L@00074
	JMP REOF	;GOT EOF
L@00074:
;
;READ ERROR
;
	CALL ERXIT
	DB	'++FILE READ ERROR$'
;
RDSECOK:	MOV BX,OFFSET 80H
	ADD BX,DX	;TO NEXT BUFF
	XCHG BX,DX	;BUFF TO DE
	INC CL	;MORE SECTORS?
	MOV AL,CL	;GET COUNT
	CMP AL,16	;DONE?
	JNZ L@00075
	JMP RDBFULL	;..YES, BUFF IS FULL
L@00075:
	JMP RDSECLP	;READ MORE
;
REOF:	MOV AL,1
	MOV  EOFLG,AL	;SET EOF FLAG
	MOV AL,CL
;
;BUFFER IS FULL, OR GOT EOF
;
RDBFULL:	MOV  SECINBF,AL	;STORE SECTOR COUNT
	MOV BX,OFFSET DBUF	;INIT BUFFER..
	MOV SECPTR,BX	;..POINTER
	MOV DX,OFFSET 80H	;RESET..
	MOV CL,STDMA	;..DMA..
	CALL BDOS	;..ADDR
	JMP RDSECT	;PASS SECT TO CALLER
;
;---->	WRSECT: WRITE A SECTOR
;
;WRITES THE SECTOR INTO A BUFFER.  WHEN 16
;HAVE BEEN WRITTEN, WRITES THE BLOCK TO DISK.
;
;ENTRY POINT "WRBLOCK" FLUSHES THE BUFFER AT EOF.
;
WRSECT:	MOV BX,SECPTR	;GET BUFF ADDR
	XCHG BX,DX	;TO DE FOR MOVE
	MOV BX,OFFSET 80H	;FROM HERE
	CALL MOVE128	;MOVE TO BUFFER
	XCHG BX,DX	;SAVE NEXT..
	MOV SECPTR,BX	;..BLOCK POINTER
	MOV AL,SECINBF	;BUMP THE..
	INC AL	;..SECTOR #..
	MOV  SECINBF,AL	;..IN THE BUFF
	CMP AL,16	;HAVE WE 16?
	JZ L@00076
	RET	;NO, RETURN
L@00076:
;
;---->	WRBLOCK: WRITES A BLOCK TO DISK
;
WRBLOCK:	MOV AL,SECINBF	;# SECT IN BUFFER
	OR AL,AL	;0 MEANS END OF FILE
	JNZ L@00077
	RET	;NONE TO WRITE
L@00077:
	MOV CL,AL	;SAVE COUNT
	MOV DX,OFFSET DBUF	;POINT TO DISK BUFF
DKWRLP:	PUSH BX
	PUSH DX
	PUSH CX
	MOV CL,STDMA	;SET DMA
	CALL BDOS	;TO BUFFER
	MOV DX,OFFSET FCB	;THEN WRITE
	MOV CL,WRITE	;..THE..
	CALL BDOS	;..BLOCK
	POP CX
	POP DX
	POP BX
	OR AL,AL
	JZ L@00078
	JMP WRERR	;OOPS, ERROR
L@00078:
	MOV BX,OFFSET 80H	;LENGTH OF 1 SECT
	ADD BX,DX	;HL= NEXT BUFF
	XCHG BX,DX	;TO DE FOR SETDMA
	DEC CL	;MORE SECTORS?
	JZ L@00079
	JMP DKWRLP	;..YES, LOOP
L@00079:
	XOR AL,AL	;GET A ZERO
	MOV  SECINBF,AL	;RESET # OF SECTORS
	MOV BX,OFFSET DBUF	;RESET BUFFER..
	MOV SECPTR,BX	;..POINTER
;
	MOV DX,OFFSET 80H	;07/13/79 MOD..
	MOV CL,STDMA	;..TO RESET..
	CALL BDOS	;..DMA ADDR.
;
	RET
;
WRERR:	MOV CL,CAN	;CANCEL..
	CALL SEND	;..SENDER
	CALL ERXIT	;EXIT W/MSG:
	DB	'++ERROR WRITING FILE',CR,LF,'$'
;
;---->	RECV: RECEIVE A CHARACTER
;
;TIMEOUT TIME IS IN B, IN SECONDS.  ENTRY VIA
;"RECVDG" DELETES GARBAGE CHARACTERS ON THE
;LINE.  FOR EXAMPLE, HAVING JUST SENT A SECTOR,
;CALLING RECVDG WILL DELETE ANY LINE-NOISE-INDUCED
;CHARACTERS "LONG" BEFORE THE ACK/NAK WOULD
;BE RECEIVED.
;
RECVDG	EQU	$	;RECEIVE W/GARBAGE DELETE
	IN AL,MODDATP	;GET A CHAR
	IN AL,MODDATP	;..TOTALLY PURGE UART
;
RECV:	PUSH DX	;SAVE
;
	IF	FASTCLK	;4MHZ?
	MOV AL,CH	;GET TIME REQUEST
	ADD AL,AL	;DOUBLE IT
	MOV CH,AL	;NEW TIME IN B
	ENDIF
;
MSEC:	MOV DX,OFFSET 50000	;1 SEC DCR COUNT
	CALL CKABORT	;CHECK FOR EXIT REQUEST
MWTI:	IN AL,MODCTLP	;CHECK STATUS
	AND AL,MODRCVB	;ISOLATE BIT
	CMP AL,MODRCVR	;READY?
	JNZ L@00080
	JMP MCHAR	;GOT CHAR
L@00080:
	DEC DL	;COUNT..
	JZ L@00081
	JMP MWTI	;..DOWN..
L@00081:
	DEC DH	;..FOR..
	JZ L@00082
	JMP MWTI	;..TIMEOUT
L@00082:
	DEC CH	;MORE SECONDS?
	JZ L@00083
	JMP MSEC	;YES, WAIT
L@00083:
;
;MODEM TIMED OUT RECEIVING
;
	POP DX	;RESTORE D,E
	STC	;CARRY SHOWS TIMEOUT
	RET
;
;GOT CHAR FROM MODEM
;
MCHAR:	IN AL,MODDATP	;READ THE CHAR
	POP DX	;RESTORE DE
;
;CALC CHECKSUM
;
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE THE CHAR
	ADD AL,CL	;ADD TO CHECKSUM
	MOV CL,AL	;SAVE CHECKSUM
;
;CHECK IF MONITORING REC'D DATA
;
	MOV AL,RSEEFLG	;SEE RECEIVED..
	OR AL,AL	;..DATA?
	JNZ L@00084
	JMP MONIN	;..YES
L@00084:
;
;CHECK IF "VIEWING" AND THIS IS A DATA CHAR
;
	MOV AL,VSEEFLG	;VIEWING..
	OR AL,AL	;..DATA?
	JZ L@00085
	JMP NOMONIN	;..NO
L@00085:
;
;"VIEW" REQUESTED.  SHOW THE CHAR IT IS DATA
;
	MOV AL,DATAFLG	;GET DATA FLAG
	OR AL,AL	;TEST IT
	JNZ L@00086
	JMP NOMONIN	;..OFF, NOT DATA
L@00086:
MONIN:	POP AX
	XCHG AL,AH
	SAHF	;..IS DATA,
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;GET IT,
	CALL SHOW	;..AND SHOW IT
NOMONIN:	POP AX
	XCHG AL,AH
	SAHF	;RESTORE CHAR
	OR AL,AL	;CARRY OFF: NO ERROR
	RET	;FROM "RECV"
;
;---->	SEND: SEND A CHARACTER TO THE MODEM
;
SEND:	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE THE CHAR
;
;CHECK IF MONITORING SENT DATA
;
	MOV AL,SSEEFLG	;CHECK IF MONITORING..
	OR AL,AL	;..SENT DATA
	JNZ L@00087
	JMP MONOUT	;..YES
L@00087:
;
;CHECK IF "VIEWING" THE FILE
;
	MOV AL,VSEEFLG	;GET VIEW FLAG
	OR AL,AL	;TEST IT
	JZ L@00088
	JMP NOMONOT	;NO
L@00088:
	MOV AL,DATAFLG	;IS THIS
	OR AL,AL	;..DATA?
	JNZ L@00089
	JMP NOMONOT	;..NO.
L@00089:
MONOUT:	POP AX
	XCHG AL,AH
	SAHF	;GET THE CHAR
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE IT
	CALL SHOW	;SHOW IT
NOMONOT:	POP AX
	XCHG AL,AH
	SAHF	;RESTORE CHAR
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE IT
	ADD AL,CL	;CALC CKSUM
	MOV CL,AL	;SAVE CKSUM
SENDW:	IN AL,MODCTLP	;GET STATUS
	AND AL,MODSNDB	;ISOLATE READY BIT
	CMP AL,MODSNDR	;READY?
	JZ L@00090
	JMP SENDW	;..NO, WAIT
L@00090:
	POP AX
	XCHG AL,AH
	SAHF	;GET CHAR
	OUT MODDATP,AL	;OUTPUT IT
	RET	;FROM "SEND"
;
;---->	WAITNAK: WAITS FOR INITIAL NAK
;
;TO ENSURE NO DATA IS SENT UNTIL THE RECEIVING
;PROGRAM IS READY, THIS ROUTINE WAITS FOR THE
;THE FIRST TIMEOUT-NAK FROM THE RECEIVER.  (E)
;CONTAINS THE # OF SECONDS TO WAIT.
;
WAITNAK:	MOV AL,VSEEFLG	;VIEWING?
	OR AL,AL
	JNZ L@00091
	JMP WAITNPR	;PRINT MSG
L@00091:
	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00092
	JMP WAITNLP	;YES, SKIP MSG
L@00092:
WAITNPR:	CALL ILPRT	;PRINT:
	DB	'AWAITING INITIAL NAK',CR,LF,0
WAITNLP:	CALL CKABORT	;ABORT IF LOCAL CTL-X
	MOV CH,1	;TIMEOUT DELAY
	CALL RECV	;DID WE GET..
	CMP AL,NAK	;..A NAK?
	JNZ L@00093
	RET	;YES, SEND BLOCK
L@00093:
;
	IF CANFLG
	CMP AL,CAN	;CTL-X FROM REMOTE?
	JNZ L@00094
	JMP ABORT	;..YES, ABORT
L@00094:
	ENDIF
;
	DEC DL	;80 TRIES?
	JNZ L@00095
	JMP ABORT	;YES, ABORT
L@00095:
	JMP WAITNLP	;NO, LOOP
;
;---->	INITADR: INIT'S CP/M BDOS ADDRESSES
;
;THIS ROUTINE FILLS IN THE ADDRESSES OF VARIOUS
;JMP AND CALL INSTRUCTIONS, SO THAT CP/M BDOS
;IS BYPASSED WHILE ACCESSING THE CONSOLE.  THIS
;IS DONE TO ALLOW CHARACTERS SUCH AS CONTROL-C
;AND CONTROL-S TO BE KEYED WHILE IN TERMINAL
;MODE, WITHOUT CP/M INTERPRETING THEM.
;
;
;---->	PROCOPT: PROCESS COMMAND OPTIONS
; 
;1) SAVES THE PRIMARY OPTION IN 'OPTION';
;2) IF OPTION 'H' (HELP) IS ASKED FOR,
;TRANSFERS DIRECTLY TO THE HELP PRINT;
;3) SCANS THE SUB-OPTION CHARACTERS, AND FOR
;EACH FOUND, ZEROS THE APPROPRIATE ENTRY IN
;THE OPTION TABLE.  FOR EXAMPLE, IF 'D' IS 
;CODED (DISCONNECT) THEN THE 'D' STORED AT
;'DISCFLG' IS SET TO 0 SO IT CAN BE TESTED
;LATER.
;
PROCOPT:	MOV DX,OFFSET FCB+1	;TO PRIMARY OPT.
	XCHG BX,DX
	MOV AL,[BX]
	XCHG BX,DX	;GET PRIMARY
	MOV  OPTION,AL	;SAVE IT
	CMP AL,'H'	;MODEM H(ELP)?
	JNZ L@00096
	JMP HELP	;..YES, GIVE HELP
L@00096:
;
OPTLP:	INC DX	;TO SECONDARY OPTION
	XCHG BX,DX
	MOV AL,[BX]
	XCHG BX,DX	;GET CHAR
;
;IF YOU MOD THIS PROGRAM FOR >7 OPTIONS,
;YOU MUST CHANGE THE FOLLOWING, SINCE
;THERE WON'T BE A ' ' AFTER THE OPTION
;IF A BAUD RATE WAS SPECIFIED.
;
	CMP AL,' '	;NO MORE OPT'NS?
	JNZ L@00097
	JMP ENDOPT	;..YES
L@00097:
;SET THE APPROP. OPT: STORE 0 IN IT
	MOV BX,OFFSET OPTBL	;HL = ADDR OF 'OAQDSRV'
	MOV cx,tablesize	;OPT TABLE LEN
OPTCK:	CMP AL,BYTE PTR [BX]	;FOUND THE OPTION?
	JZ L@00098
	JMP OPTNO	;NO, DON'T SET IT
L@00098:
	MOV BYTE PTR [BX],0	;SET THE OPTION
	JMP OPTLP	;GET NEXT OPTION
OPTNO:	INC BX	;TO NEXT
	DEC CH	;MORE?
	JZ L@00099
	JMP OPTCK
L@00099:
;OPTION NOT IN TABLE
	JMP BADOPT	;SHOW BAD SUB OPTION
;
;IF "VIEW" WAS ASKED FOR, SET QUIET FLAG
;
ENDOPT:	MOV AL,VSEEFLG	;VIEW..
	OR AL,AL	;..ASKED FOR?
	JZ L@00100
	RET	;..NO, RET FROM 'PROCOPT'
L@00100:
	MOV  QFLG,AL	;YES, NO HDR/CKSUM PRT
	RET	;FROM 'PROCOPT'
;
;DONE - CLOSE UP SHOP
;
DONE:	MOV AL,VSEEFLG	;VIEWING?
	OR AL,AL
	JNZ L@00101
	JMP DONETC	;SHOW MSG
L@00101:
	MOV AL,QFLG	;QUIET
	OR AL,AL	;..MODE?
	JNZ L@00102
	JMP DONECTE	;YES, CK TERM/ECHO
L@00102:
DONETC:	CALL ILPRT
	DB	CR,LF,'TRANSFER COMPLETE' 
	DB	CR,LF,0
;
;CHECK IF TERMINAL OR ECHO SUB COMMAND
;WAS SPECIFIED
;
DONECTE:	MOV AL,TERMFLG	;TERM?
	OR AL,AL
	JNZ L@00103
	JMP TERM	;..YES
L@00103:
	MOV AL,ECHOFLG	;ECHO?
	OR AL,AL
	JNZ L@00104
	JMP TRMECHO	;..YES
L@00104:
;
;FALL INTO 'CKDIS'
;
;---->	CKDIS: CHECK IF DISCONNECT REQUESTED
;
;THIS ROUTINE IS JUMPED TO AT THE END OF
;PROCESSING, AND DISCONNECTS THE PHONE IF
;'D' WAS SPECIFIED AS A SUB-OPTION.
;
CKDIS:	MOV AL,DISCFLG	;CHECK 'D' FLAG
	OR AL,AL	;REQUESTED?
;
	IF	PMMI
	JZ L@00105
	JMP NDIS	;..NO, JUST EXIT
L@00105:
	ENDIF
;
	IF	NOT PMMI
	JZ L@00106
	JMP EXIT
L@00106:
	ENDIF
;
;AWAIT C/R TO DISC. SO WE DON'T LOSE THE PHONE
;
	CALL ILPRT
	DB	CR,LF,'PRESS RETURN TO DISCONNECT:',0
	CALL KEYIN
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH
	CALL CRLF
	POP AX
	XCHG AL,AH
	SAHF
	CMP AL,0DH
	JZ L@00107
	JMP CKDIS	;ASK AGAIN
L@00107:
;
;---->	DISCONN: DISCONNECT THE PHONE
;
DISCONN	EQU	$
;
	IF	PMMI
	XOR AL,AL	;GET DISCONN VALUE
	OUT MODCTLP,AL	;RESET ORIG/ANSW
	OUT MODCTL2,AL	;TURN OFF DTR, DO BREAK
	ENDIF		;PMMI
;
	CALL ILPRT	;PRINT:
	DB	'++DISCONNECTED++',0
	JMP EXIT
;
;NO DISCONNECT, tipe MSG AS REMINDER THAT PHONE'S
;ON HOOK
;
	IF	PMMI
NDIS:	MOV AL,QFLG	;QUIET..
	OR AL,AL	;..MODE?
	JNZ L@00108
	JMP EXIT	;..YES, NO MSG
L@00108:
	CALL ILPRT
	DB	CR,LF,'++Don''t forget - The modem is '
	DB	'NOT DISCONNECTED++',CR,LF
	DB	'USE "MODEM D" to disconnect',CR,LF,0
	JMP EXIT
;
	ENDIF
;
;---->	INITMOD: INITIALIZED THE MODEM
;
;THIS ROUTINE IS USED TO INITIALIZE SERIAL
;BOARDS, OR SETUP S-100 MODEM BOARDS.
;JUST RETURNS IF NO INITIALIZATION REQUIRED.
;
INITMOD:
;
	IF	INITREQ  ;REQUIRE INIT?
	MOV AL,INITC1	;GET 1ST INIT CHAR
	OUT MODCTLP,AL	;OUTPUT IT
	MOV AL,INITC2	;GET 2ND INIT CHAR
	OUT MODCTLP,AL	;OUTPUT IT
	ENDIF
;
	IF	PMMI
	CALL GETBAUD	;GET THE BAUD RATE
	OUT BAUDRP,AL	;OUT BAUD RATE PORT
;
;SET THE MOTOROLA MODEM CHIP BIT FOR >300 IF REQ'D
;
	CMP AL,52	;>300?
	MOV AL,5FH	;VALUE FOR >300
	JAE L@00109
	JMP GT300
L@00109:
	MOV AL,7FH	;VALUE FOR <= 300
GT300:	OUT MODCTL2,AL	;SET IT
;
;SET ORIG/ANSW IF REQUESTED
;
	MOV AL,ORIGFLG	;ORIGINATE..
	OR AL,AL	;..MODE?
	MOV AL,ORIGMOD
	JNZ L@00110
	JMP OFFHOOK	;..YES, DO IT
L@00110:
	MOV AL,ANSWFLG	;ANSWER..
	OR AL,AL	;..MODE?
	MOV AL,ANSWMOD
	JZ L@00111
	RET	;NEITHER ORIG NOR ANSW.
L@00111:
;
;GO OFFHOOK IN REQUESTED (ORIG/ANSW) MODE
;
OFFHOOK:	MOV BX,OFFSET 4000	;DELAY AMT
OFFDLY:	DEC BL
	JZ L@00112
	JMP OFFDLY
L@00112:
	DEC BH
	JZ L@00113
	JMP OFFDLY
L@00113:
	OUT MODCTLP,AL	;GO OFF HOOK
	RET
;
;---->	GETBAUD: GETS BAUD RATE FROM COMMAND
;
;THIS ROUTINE CHECKS IF A BAUD RATE HAS
;BEEN ASKED FOR, (SUCH AS MODEM T.450),
;AND IF SO, CALCULATES THE PMMI BAUD RATE
;VALUE TO BE OUTPUT.  DEFAULTS TO 300.
;
GETBAUD:	MOV AL,FCB+9	;GET 'FILEtipe'
	CMP AL,' '	;DEFAULT?
	MOV AL,52	;300 BAUD VALUE
	JNZ L@00114
	RET	;NO BAUD RATE, USE 300
L@00114:
;
;GOT BAUD RATE - CONVERT TO PROPER TIMER VALUE
;
;FIRST, CONVERT THE NUMBER TO BINARY
;
	MOV DX,OFFSET FCB+9	;TO ASCII VALUE
	MOV BX,OFFSET 0	;INIT BINARY RESULT
DECLP:	XCHG BX,DX
	MOV AL,[BX]
	XCHG BX,DX	;GET ASCII DIGIT
	INC DX	;TO NEXT DIGIT
	CMP AL,' '	;BLANK ONE?
	JNZ L@00115
	JMP DECLP	;..YES, SKIP IT
L@00115:
	CMP AL,'0'	;VALIDATE IT
	JAE L@00116
	JMP BADRATE	;ERROR
L@00116:
	CMP AL,'9'+1	;VALIDATE
	JNAE L@00117
	JMP BADRATE	;ERROR
L@00117:
	SUB AL,'0'	;MAKE DIGIT BINARY
;
;MULTIPLY PREV VALUE BY 10
;
	MOV CH,BH	;SET UP FOR
	MOV CL,BL	;MULTIPLY BY 10
	ADD BX,BX	;MULTIPLY BY 2
	ADD BX,BX	;X 2 = 4
	ADD BX,CX	;+ 1 = 5
	ADD BX,BX	;X 2 = 10
	ADD AL,BL	;ADD IN DIGIT
	MOV BL,AL	;SAVE BACK
	JZ L@00118
	JMP DIGNC	;NO CARRY?
L@00118:
	INC BH	;ADD IN CARRY
;CHECK IF DONE?
DIGNC:	MOV AL,DL	;SEE IF PAST
	CMP AL,FCB+12	;..LAST DIGIT
	JZ L@00119
	JMP DECLP	;NO, LOOP
L@00119:
;
;CALCULATE THE VALUE TO OUTPUT:
;
;	RATE = 250000/16/BAUD RATE
;
;	DIVIDE BY REPETITIVE SUBTRACTION
;	------
;COMPLEMENT THE BAUD RATE
;
	MOV AL,BH	;GET HI
	NOT AL	;COMPLEMENT
	MOV DH,AL	;SAVE
	MOV AL,BL	;GET LO
	NOT AL	;COMPLEMENT
	MOV DL,AL	;SAVE
	INC DX	;DE=2'S COMPLEMENT
;DIVIDE
	MOV BX,OFFSET 15625	;250000/16 
	MOV CX,OFFSET -1	;INIT QUOTIENT
DIVLP:	INC CX	;BUMP QUOTIENT
	ADD BX,DX	;'SUBTRACT'
	JAE L@00120
	JMP DIVLP	;LOOP 'TILL DONE
L@00120:
;VALIDATE THE RESULT
	MOV AL,CH	;CAN'T HAVE >255
	OR AL,AL
	MOV AL,CL	;GET ACTUAL
	JNZ L@00121
	RET	;RET IF <256
L@00121:
;
;INVALID BAUD RATE
;
BADRATE:	CALL ERXIT
	DB	'++INVALID BAUD RATE++$'
	ENDIF		;PMMI
	RET
;
;---->	MOVEFCB: MOVES FCB(2) TO FCB
;
;I ATTEMPTED TO MAKE THE MODEM COMMAND 'NATURAL',
;I.E. MODEM SEND FILENAME (MODEM S FN.FT) RATHER
;THAT MODEM FILENAME SEND (MODEM FN.FT S) SO THIS
;ROUTINE MOVES THE FILENAME FROM THE SECOND FCB
;TO THE FIRST
;
MOVEFCB:	MOV BX,OFFSET FCB+16	;FROM
	MOV DX,OFFSET FCB	;TO
	MOV CH,16	;LEN
	CALL MOVE	;DO THE MOVE
	XOR AL,AL	;GET 0
	MOV  FCBSNO,AL	;ZERO SECTOR #
	MOV  FCBEXT,AL	;..AND EXTENT
	RET
;
;---->	SHOW: SHOWS CHAR SENT/RECEIVED
;
;CR, LF, AND TAB ARE SHOWN.  ALL OTHER
;NON-PRINTABLE CHARACTERS ARE SHOWN IN
;HEX AS (XX)
;
SHOW:	CMP AL,LF	;LF?
	JNZ L@00122
	JMP Ctipe	;..YES, tipe IT
L@00122:
	CMP AL,CR	;CR?
	JNZ L@00123
	JMP Ctipe	;..YES, tipe IT
L@00123:
	CMP AL,09	;TAB
	JNZ L@00124
	JMP Ctipe	;..YES, tipe IT
L@00124:
	CMP AL,' '	;CTL-CHR?
	JAE L@00125
	JMP SHOWHEX	;YES, SHOW IN HEX
L@00125:
	CMP AL,7FH	;DEL?
	JAE L@00126
	JMP Ctipe	;NO, tipe THE CHAR
L@00126:
SHOWHEX:	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE THE CHAR
	MOV AL,'('	;tipe..
	CALL Ctipe	;..'('
	POP AX
	XCHG AL,AH
	SAHF	;THEN..
	CALL HEXO	;..THE CHAR
	MOV AL,')'	;THEN..
	JMP Ctipe	;..')' AND RETURN.
;
;---->	Ctipe: tipeS VIA CP/M SO TABS ARE EXPANDED
;
Ctipe:	PUSH CX	;SAVE..
	PUSH DX	;..ALL..
	PUSH BX	;..REGS
	MOV DL,AL	;CHAR TO E
	MOV CL,WRCON	;GET BDOS FNC
	CALL BDOS	;PRIN THE CHR
	POP BX	;RESTORE..
	POP DX	;..ALL..
	POP CX	;..REGS
	RET	;FROM "Ctipe"
;
CRLF:	MOV AL,CR
	CALL tipe
	MOV AL,LF
;
;---->	tipe: tipe VIA DIRECT BDOS ACCESS
;
;THIS ROUTINE BYPASSES CP/M'S CTL-S, CTL-C
;TESTS.
;
tipe:	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE CHAR
	PUSH CX	;AND B
Vtipe:	mov	cl,6
	call	bdos
	POP CX	;RESTORE BC
	POP AX
	XCHG AL,AH
	SAHF	;..AND CHAR
	RET	;FROM "tipe"
;
;KEYBOARD STATUS
;
Stat:	mov	cl,6
	mov	dl,0feh
	call	bdos
	OR AL,AL	;0 => NOT READY
	RET
;
;KEYBOARD INPUT
;
keyin:	mov	cl,6
	mov	dl,0ffh
	call	bdos
	or	al,al
	jz	keyin
	ret
;
;HEX OUTPUT
;
HEXO:	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE FOR RIGHT DIGIT
	RCR AL,1	;RIGHT..
	RCR AL,1	;..JUSTIFY..
	RCR AL,1	;..LEFT..
	RCR AL,1	;..DIGIT..
	CALL NIBBL	;PRINT LEFT DIGIT
	POP AX
	XCHG AL,AH
	SAHF	;RESTORE RIGHT
NIBBL:	AND AL,0FH	;ISOLATE DIGIT
	CMP AL,10	;IS IS <10?
	JAE L@00127
	JMP ISNUM	;YES, NOT ALPHA
L@00127:
	ADD AL,7	;ADD ALPHA BIAS
ISNUM:	ADD AL,'0'	;MAKE PRINTABLE
	JMP tipe	;..THEN tipe IT
;
;---->	CKQUIT: QUIT/RETRY AFTER MULTIPLE ERRS.
;
;RETURNS W/ ZERO SET IF "RETRY" ASKED FOR
;
CKQUIT:	XOR AL,AL	;ZERO..
	MOV  ERRCT,AL	;..ERROR COUNT
	CALL ILPRT	;PRINT:
	DB	'MULTIPLE ERRORS ENCOUNTERED.  '
	DB	'tipe Q TO QUIT, R TO RETRY: ',0
	CALL KEYIN	;QUIT/RETRY
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH
	CALL CRLF
	POP AX
	XCHG AL,AH
	SAHF
	AND AL,5FH	;MAKE UPPER CASE
	CMP AL,'R'	;RETRY?
	JNZ L@00128
	RET	;'KEEP ON TRUCKIN'
L@00128:
	CMP AL,'Q'	;QUIT?
	JZ L@00129
	JMP CKQUIT	;NO, ASK AGAIN
L@00129:
	OR AL,AL	;SET NON-ZERO
	RET
;
;---->	ILPRT: INLINE PRINT OF MSG
;
;THE CALL TO ILPRT IS FOLLOWED BY A MESSAGE,
;BINARY 0 AS THE END.  BINARY 1 MAY BE USED TO
;PAUSE (MESSAGE 'PRESS RETURN TO CONTINUE')
;
ILPRT:	POP SI
	XCHG BX,SI
	PUSH SI	;SAVE HL, GET HL=MSG
ILPLP:	MOV AL,BYTE PTR [BX]	;GET CHAR
	OR AL,AL	;END OF MSG?
	JNZ L@00130
	JMP ILPRET	;..YES, RETURN
L@00130:
	CMP AL,1	;PAUSE?
	JNZ L@00131
	JMP ILPAUSE	;..YES
L@00131:
	CALL Ctipe	;tipe THE MSG
ILPNEXT:	INC BX	;TO NEXT CHAR
	JMP ILPLP	;LOOP
;
;PAUSE WHILE TYPING HELP SO INFO DOESN'T
;	SCROLL OFF OF VIDEO SCREENS
;
ILPAUSE:	CALL ILPRT	;PRINT:
	DB	CR,LF,'PRESS RETURN TO CONTINUE'
	DB	CR,LF,0
	CALL KEYIN	;GET ANY CHAR
	CMP AL,'C'-40H	;REBOOT?
	JNZ L@00132
	JMP EXIT	;YES.
L@00132:
	JMP ILPNEXT	;LOOP
;
ILPRET:	INC BX
	POP SI
	XCHG BX,SI
	PUSH SI	;RESTORE HL
	RET	;PAST MSG
;
;---->	PRTMSG: PRINTS MSG POINTED TO BY (DE)
;
;A '$' IS THE ENDING DELIMITER FOR THE PRINT.
;NO REGISTERS SAVED.
;
PRTMSG:	MOV CL,PRINT	;GET BDOS FNC
	JMP BDOS	;PRINT MESSAGE, RETURN
;
;---->	ERXIT: EXIT PRINTING MSG FOLLOWING CALL
;
ERXIT:	POP DX	;GET MESSAGE
	CALL PRTMSG	;PRINT IT
	CALL CKDIS	;DISCONNECT?
;
EXIT:	mov	cl,0
	mov	dl,0
	call	bdos
;
;MOVE 128 CHARACTERS
;
MOVE128:	MOV CH,128	;SET MOVE COUNT
;
;MOVE FROM (HL) TO (DE) LENGTH IN (B)
;
MOVE:	MOV AL,BYTE PTR [BX]	;GET A CHAR
	XCHG BX,DX
	MOV [BX],AL
	XCHG BX,DX	;STORE IT
	INC BX	;TO NEXT "FROM"
	INC DX	;TO NEXT "TO"
	DEC CH	;MORE?
	JZ L@00133
	JMP MOVE	;..YES, LOOP
L@00133:
	RET	;..NO, RETURN
;	----------------
OPTION	DB	0	;PRIMARY OPTION
;
;DATAFLG IS USED BY THE "V" SUBCOMMAND -
;IT IS 0 WHEN A HEADER OR CKSUM IS BEING
;SENT/RCD, AND 1 IF "VIEWABLE" DATA (THE
;SECTOR ITSELF) IS
;
DATAFLG	DB	0	;AT HEADER, FIRST
;
;
;SUB-OPTION TABLE.  IF AN OPTION IS IN EFFECT,
;	THE CHARACTER IS SET TO BINARY 0
;
OPTBL	EQU	offset $
ANSWFLG	DB	'A'	;ANSWER MODE
DISCFLG	DB	'D'	;DISCONNECT WHEN DONE
ECHOFLG	DB	'E'	;TO ECHO AFTER XFER
ORIGFLG	DB	'O'	;ORIGINATE MODE
QFLG	DB	'Q'	;QUIET TRANSFER (NO MSGS)
RSEEFLG	DB	'R'	;SEE WHAT'S RECEIVED
SSEEFLG	DB	'S'	;SEE WHAT'S SENT
TERMFLG	DB	'T'	;TO TERM AFTER XFER
VSEEFLG	DB	'V'	;VIEW MESSAGES (NO HDR, ETC)
;
OPTBE	EQU	offset $	;END OF OPTIONS
tablesize	equ	optbe-optbl
;
RCVSNO	DB	0	;SECT # RECEIVED
SECTNO	DB	0	;CURRENT SECTOR NUMBER 
ERRCT	DB	0	;ERROR COUNT
;FOLLOWING 3 USED BY DISK BUFFERING ROUTINES
EOFLG	DB	0	;EOF FLAG (1=TRUE)
SECPTR	DW	DBUF
SECINBF	DB	0	;# OF SECTORS IN BUFFER
;
;16 SECTOR DISK BUFFER (OVERLAYS HELP MSGS)
;
DBUF	EQU	$	;16 SECTOR DISK BUFFER
;
;INVALID COMMAND
;
BADOPT:	CALL tipe
	CALL ILPRT	;EXIT W/ERROR
	DB	': INVALID OPTION ON MODEM '
	DB 'COMMAND - ',CR,LF
	DB 'PRESS RETURN FOR HELP, CTL-C IF NOT',CR,LF,1,0
;
HELP:	CALL ILPRT
	DB 'Format for command is:',cr,lf,cr,lf
	DB 'MODEM # FILENAME',CR,LF,CR,LF
	DB 'Where # is a 1 character primary option,',cr,lf
	DB ' which may be followed by sub-options,',cr,lf
	DB ' and by ".xxx" to set baud rate to xxx'
	DB cr,lf,cr,lf,1
	DB 'Primary Options:',cr,lf
	DB '	S to send a file',cr,lf
	DB '	R to receive a file',cr,lf
	DB '	T to act as a terminal',cr,lf
	DB '	E to act as a computer (echo data)',cr,lf
	DB '	D to disconnect the phone'
	DB '	(S100 modems only)',cr,lf
	DB '	H to print this help file'
	DB cr,lf,cr,lf,1
	DB 'Secondary options:',cr,lf
	DB '	A answer mode',cr,lf
	DB '	O originate mode',cr,lf
	DB '	D disconnect after execution',cr,lf
	DB '	T go to terminal mode after file xfer',cr,lf
	DB '	E go to echo mode after file xfer',cr,lf
	DB '	Q quiet mode - no status msgs',cr,lf
	DB '	R show chars received',cr,lf
	DB '	S show chars sent',cr,lf
	DB '	V view file sent/received (no status)',cr,lf
	DB CR,LF,'FOR EXAMPLES, tipe: MODEM X',cr,lf,0
	JMP EXIT
;
EXAM:	CALL ILPRT
	DB 'Send file, originate mode, 300 baud:',CR,LF
	DB '	MODEM SO fn.ft',cr,lf
	DB 'Send another file:',CR,LF
	DB '	MODEM S fn.ft',cr,lf
	DB 'Then send a third file at 450 baud and disconnect'
	DB CR,LF,'	MODEM SD.450 fn.ft',cr,lf
	DB 'Act as a terminal at 110 baud',cr,lf
	DB '	MODEM T.110',CR,LF
	DB ' Use ctl-D to disconnect)',cr,lf
	DB 'Receive file, answ mode, view it, 600 baud:',cr,lf
	DB '	MODEM RAV.600 fn.ft',cr,lf,0
JMP:	EXIT
bdos:	int	224
	ret
;
;
; BDOS EQUATES (VERSION 2)
;
RDCON	EQU	1
WRCON	EQU	2
PRINT	EQU	9
CONST	EQU	11	;CONSOLE STAT
OPEN	EQU	15	;0FFH=NOT FOUND
CLOSE	EQU	16	;	"	"
SRCHF	EQU	17	;	"	"
SRCHN	EQU	18	;	"	"
ERASE	EQU	19	;NO RET CODE
READ	EQU	20	;0=OK, 1=EOF
WRITE	EQU	21	;0=OK, 1=ERR, 2=?, 0FFH=NO DIR SPC
MAKE	EQU	22	;0FFH=BAD
REN	EQU	23	;0FFH=BAD
STDMA	EQU	26	;SET DMA
REIPL	EQU	0
;
	org	5ch
FCB	rb	34
FCBEXT	EQU	FCB+12	;FILE EXTENT
FCBSNO	EQU	FCB+32	;SECTOR #
FCB2	EQU	fcb+10h
